package com.jkgh.remedium.tools;

import com.jkgh.asin.StateMachine;
import com.jkgh.asin.buffers.DataConsumer;
import com.jkgh.remedium.objects.RemediumObject;
import com.jkgh.remedium.objects.messages.RemediumOutMessage;
import com.jkgh.remedium.states.Future;
import com.jkgh.remedium.states.GatheringState;

public class RemediumTools {

	public static void produceString(DataConsumer consumer, String s) { // TODO: test if faster then .getBytes()
		if (s == null) {
			consumer.consumeInt(-1);
		} else {
			int length = s.length();
			consumer.consumeInt(length);
			for (int i=0; i<length; ++i) {
				consumer.consumeByte((byte) s.charAt(i));
			}
		}
	}
	
	public static void produceStringArray(DataConsumer consumer, String[] ss) {
		if (ss == null) {
			consumer.consumeInt(-1);
		} else {
			consumer.consumeInt(ss.length);
			for (String s: ss) {
				RemediumTools.produceString(consumer, s);
			}
		}
	}

	public static void consumeString(final StateMachine machine, final Future<String> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				if (length > 0) {
					machine.setCurrentState(new GatheringState(machine, length) {
						
						@Override
						protected void onNeededBytesAvailable() {
	
							StringBuilder ret = new StringBuilder(length);
							for (int i=0; i<length; ++i) {
								ret.append((char) machine.produceByte());
							}
							
							future.goWith(ret.toString());
						}
					});
				} else if (length == 0) {
					future.goWith("");
				} else {
					future.goWith(null);
				}
			}
		});
	}
	
	public static void consumeStringArray(final StateMachine machine, final Future<String[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new String[0]);
				} else {
					consumeString(machine, new Future<String>() {

						private int i = 0;
						private String[] result = new String[length];
						
						@Override
						public void goWith(String o) {
							result[i++] = o;
							if (i == length) {
								future.goWith(result);
							} else {
								consumeString(machine, this);
							}
						}
					});
				}
			}
		});
	}

	public static void consumeInt(StateMachine machine, final Future<Integer> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				int ret = machine.produceInt();
				future.goWith(ret);
			}
		});
	}
	
	public static void produceIntArray(DataConsumer consumer, int[] is) {
		if (is == null) {
			consumer.consumeInt(-1);
		} else {
			consumer.consumeInt(is.length);
			for (int i: is) {
				consumer.consumeInt(i);
			}
		}
	}

	public static void produceDoubleArray(DataConsumer consumer, double[] ds) {
		if (ds == null) {
			consumer.consumeInt(-1);
		} else {
			consumer.consumeInt(ds.length);
			for (double d: ds) {
				consumer.consumeDouble(d);
			}
		}
	}
	
	public static void produceByteArray(DataConsumer consumer, byte[] bs) {
		if (bs == null) {
			consumer.consumeInt(-1);
		} else {
			consumer.consumeInt(bs.length);
			consumer.consumeBytes(bs, 0, bs.length);
		}
	}
	
	public static void consumeIntArray(final StateMachine machine, final Future<int[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new int[0]);
				} else {
					machine.setCurrentState(new GatheringState(machine, 4*length) {
						
						@Override
						protected void onNeededBytesAvailable() {
							int[] ret = new int[length];
							for (int i=0; i<length; ++i) {
								ret[i] = machine.produceInt();
							}
							future.goWith(ret);
						}
					});
				}
			}
		});
	}

	public static void produceObjectArray(DataConsumer consumer, RemediumOutMessage message, RemediumObject[] objects) {
		if (objects == null) {
			consumer.consumeInt(-1);
		} else {
			consumer.consumeInt(objects.length);
			for (RemediumObject object: objects) {
				message.produceObject(object, consumer);
			}
		}
	}
	
	public static void consumeByteArray(final StateMachine machine, final Future<byte[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new byte[0]);
				} else {
					machine.setCurrentState(new GatheringState(machine, length) {
						
						@Override
						protected void onNeededBytesAvailable() {
							byte[] ret = new byte[length];
							machine.produceBytes(ret, 0, length);
							future.goWith(ret);
						}
					});
				}
			}
		});
	}
	
	public static void consumeDoubleArray(final StateMachine machine, final Future<double[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new double[0]);
				} else {
					machine.setCurrentState(new GatheringState(machine, 8*length) {
						
						@Override
						protected void onNeededBytesAvailable() {
							double[] ret = new double[length];
							for (int i=0; i<length; ++i) {
								ret[i] = machine.produceDouble();
							}
							future.goWith(ret);
						}
					});
				}
			}
		});
	}
}
